package com.xbongbong.paas.lowcode.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormEntity;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.pro.enums.errorcodes.ProErrorCodeEnum;
import com.xbongbong.sys.help.ProPermissionHelp;
import com.xbongbong.paas.log.MongoLogHelp;
import com.xbongbong.paas.log.constant.LogMemoConstant;
import com.xbongbong.paas.log.thread.LogThreadPoolBeanConfig;
import com.xbongbong.paas.lowcode.DeveloperService;
import com.xbongbong.paas.model.DeveloperModel;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.wrap.UserVO;
import com.xbongbong.pro.department.pojo.DepartmentUserPojo;
import com.xbongbong.pro.developer.pojo.DeveloperPojo;
import com.xbongbong.pro.developer.pojo.dto.DeveloperAddDTO;
import com.xbongbong.pro.developer.pojo.dto.DeveloperDeleteDTO;
import com.xbongbong.pro.developer.pojo.dto.DeveloperGetDTO;
import com.xbongbong.pro.developer.pojo.dto.DeveloperListDTO;
import com.xbongbong.pro.developer.pojo.dto.DeveloperUpdateDTO;
import com.xbongbong.pro.developer.pojo.dto.DeveloperVerifyDTO;
import com.xbongbong.pro.developer.pojo.vo.DeveloperAddVO;
import com.xbongbong.pro.developer.pojo.vo.DeveloperDeleteVO;
import com.xbongbong.pro.developer.pojo.vo.DeveloperGetVO;
import com.xbongbong.pro.developer.pojo.vo.DeveloperListVO;
import com.xbongbong.pro.developer.pojo.vo.DeveloperUpdateVO;
import com.xbongbong.pro.domain.entity.DeveloperEntity;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.enums.permission.enums.ProPermissionAliasEnum;
import com.xbongbong.pro.form.pojo.FormPojo;
import com.xbongbong.pro.log.pojo.DetailLogPojo;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.WorkOrderFormEntity;
import com.xbongbong.saas.enums.OperateModuleTypeEnum;
import com.xbongbong.saas.enums.OperateTypeEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.service.toolbox.help.LogHelp;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.help.ProPermissionHelp;
import com.xbongbong.sys.model.UserModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @author 吴峰
 * @date 2021/03/03 16:46
 */
@Service("developerService")
public class DeveloperServiceImpl implements DeveloperService {

    private static final Logger LOG = LoggerFactory.getLogger(DeveloperServiceImpl.class);

    @Resource
    private DeveloperModel developerModel;
    @Resource
    private UserModel userModel;
    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private MongoLogHelp mongoLogHelp;
    @Resource
    private LogHelp logHelp;
    @Resource
    private LogThreadPoolBeanConfig logThreadPoolBeanConfig;

    @Override
    public DeveloperListVO list(DeveloperListDTO developerListDTO) throws XbbException {
        DeveloperListVO developerListVO = new DeveloperListVO();
        try {
            UserVO userVO = developerListDTO.getLoginUser();
            boolean add = userVO.isAdminOrBoss()
                    || ProPermissionHelp.hasThisPermission(userVO, ProPermissionAliasEnum.DEVELOPER_ADD.getAlias());
            boolean view = userVO.isAdminOrBoss() || ProPermissionHelp.hasThisPermission(userVO, ProPermissionAliasEnum.DEVELOPER_VIEW.getAlias());
            if (!view) {
                developerListVO.setAdd(add);
                developerListVO.setDeveloperList(new ArrayList<>());
                return developerListVO;
            }
            String corpid = developerListDTO.getCorpid();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("orderByStr", "update_time desc");
            List<DeveloperEntity> list = developerModel.findEntitys(param);
            if (CollectionsUtil.isEmpty(list)) {
                developerListVO.setAdd(add);
                developerListVO.setDeveloperList(new ArrayList<>());
                return developerListVO;
            }
            Set<String> userSet = new HashSet<>();
            Set<Long> formIdList = new HashSet<>();
            Set<Long> workOrderFormIdList = new HashSet<>();
            list.forEach(item->{
                userSet.add(item.getDeveloper());
                List<FormPojo> list1 = JSON.parseArray(item.getDevPermission(), FormPojo.class);
                if (CollectionsUtil.isNotEmpty(list1)) {
                    list1.forEach(formPojo->{
                        if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType())) {
                            workOrderFormIdList.add(formPojo.getFormId());
                        } else {
                            formIdList.add(formPojo.getFormId());
                        }

                    });
                }
            });
            Map<Long, PaasFormEntity> formMap = proFormHelp.getFormMap(formIdList, corpid, null);
            Map<Long, WorkOrderFormEntity> workOrderFormMap = proFormHelp.getWorkOrderFormMap(workOrderFormIdList, corpid, null);
            List<UserEntity> userList =  userModel.findEntitysByUserIds(new ArrayList<>(userSet), corpid);
            Map<String, UserEntity> userMap = new HashMap<>(userList.size());
            userList.forEach(item->{
                userMap.put(item.getUserId(), item);
            });
            List<DeveloperPojo> developerList = new ArrayList<>();
            list.forEach(item->{

                // set开发人员和开发权限范围
                DeveloperPojo developerPojo = setDeveloperAndPermission(userMap, workOrderFormMap, formMap, item, developerListDTO.getLoginUser());
                developerList.add(developerPojo);
            });
            developerListVO.setAdd(add);
            developerListVO.setDeveloperList(developerList);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("developerServiceImpl.list error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return developerListVO;
    }

    @Override
    public DeveloperAddVO add(DeveloperAddDTO developerAddDTO) throws XbbException {
        DeveloperAddVO developerAddVO = new DeveloperAddVO();
        try {
            UserVO userVO = developerAddDTO.getLoginUser();
            ProPermissionHelp.verifyPermission(developerAddDTO.getLoginUser(), ProPermissionAliasEnum.CUSTOMER_ADD.getAlias());
            String corpid = developerAddDTO.getCorpid();

            UserEntity userEntity = userModel.getByKey(developerAddDTO.getUser().getId().toString(), corpid);
            if (Objects.isNull(userEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100065);
            }
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            List<DeveloperEntity> developerList = developerModel.findEntitys(param);
            if (developerList.size() >= PaasConstant.DEVELOPER_MAX_SIZE) {
                throw new XbbException(ProErrorCodeEnum.API_ERROR_260027);
            }
            for (DeveloperEntity item : developerList) {
                // 判断开发人员是否已存在
                if (Objects.equals(item.getDeveloper(), developerAddDTO.getUser().getId())) {
                    throw new XbbException(ProErrorCodeEnum.API_ERROR_260028);
                }
            }
            DeveloperEntity developerEntity = new DeveloperEntity(corpid, developerAddDTO.getUser().getId().toString(), JSON.toJSONString(developerAddDTO.getFormList()), developerAddDTO.getMemo(), developerAddDTO.getUserId());


            developerModel.insert(developerEntity);
            developerAddVO.setDeveloperId(developerEntity.getId());
            // 日志
            String userName= userVO.getName();
            String userId = userVO.getUserId();
            String memo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_ADD_DEVELOPER), userName, userEntity.getName());
            mongoLogHelp.buildLog(corpid, userId, userName, OperateModuleTypeEnum.DEVELOPER, OperateTypeEnum.NEW,
                    developerEntity.toString(), userEntity.getName(), memo, developerAddDTO.getHttpHeader());
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("developerServiceImpl.add error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return developerAddVO;
    }

    @Override
    public DeveloperUpdateVO update(DeveloperUpdateDTO developerUpdateDTO) throws XbbException {
        DeveloperUpdateVO developerUpdateVO = new DeveloperUpdateVO();
        try {
            ProPermissionHelp.verifyPermission(developerUpdateDTO.getLoginUser(), ProPermissionAliasEnum.DEVELOPER_EDIT.getAlias());

            String corpid = developerUpdateDTO.getCorpid();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put("developer", developerUpdateDTO.getUser().getId());
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            List<DeveloperEntity> developerList = developerModel.findEntitys(param);
            if (CollectionsUtil.isNotEmpty(developerList)) {
                // 判断开发人员是否已存在
                boolean same = developerList.size() == 1 && !Objects.equals(developerUpdateDTO.getDeveloperId(), developerList.get(0).getId());
                if (developerList.size()>1 || same) {
                    throw new XbbException(ProErrorCodeEnum.API_ERROR_260028);
                }
            }
            UserEntity userEntity = userModel.getByKey(developerUpdateDTO.getUser().getId().toString(), corpid);
            if (Objects.isNull(userEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100065);
            }
            DeveloperEntity developer = developerModel.getByKey(developerUpdateDTO.getDeveloperId(), corpid);
            if (Objects.isNull(developer)) {
                throw new XbbException(ProErrorCodeEnum.API_ERROR_260030);
            }
            DeveloperEntity developerEntity = new DeveloperEntity(corpid, developerUpdateDTO.getUser().getId().toString(), JSON.toJSONString(developerUpdateDTO.getFormList()), developerUpdateDTO.getMemo(), developerUpdateDTO.getUserId());

            developerEntity.setId(developerUpdateDTO.getDeveloperId());
            developerModel.update(developerEntity);
            developerUpdateVO.setDeveloperId(developerEntity.getId());
            Runnable runnable = () -> {
                try {
                    JSONObject oldObj = JSONObject.parseObject(JSONObject.toJSONString(developer));
                    JSONObject newObj = JSONObject.parseObject(JSONObject.toJSONString(developerEntity));
                    List<DetailLogPojo> detailArr = logHelp.getDetailArr4Normal(corpid, OperateModuleTypeEnum.DEVELOPER, oldObj, newObj);
                    if (detailArr != null) {
                        String memo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_UPDATE_DEVELOPER), developerUpdateDTO.getLoginUserName(), userEntity.getName());
                        //详情页有更改内容，才记录日志
                        mongoLogHelp.buildLog4DetailArr(detailArr, corpid, developerUpdateDTO.getUserId(), developerUpdateDTO.getLoginUserName(), OperateModuleTypeEnum.DEVELOPER, OperateTypeEnum.EDIT,
                                developerUpdateDTO.getDeveloperId().toString(), userEntity.getName(), memo, developerUpdateDTO.getHttpHeader());
                    }
                } catch (Exception e) {
                    LOG.error("developer.update log fail", e);
                }
            };
            logThreadPoolBeanConfig.sysLogThreadPool().execute(runnable);
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("developerServiceImpl.update error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return developerUpdateVO;
    }

    @Override
    public DeveloperDeleteVO delete(DeveloperDeleteDTO developerDeleteDTO) throws XbbException {
        DeveloperDeleteVO developerUpdateVO = new DeveloperDeleteVO();
        try {
            UserVO userVO = developerDeleteDTO.getLoginUser();
            ProPermissionHelp.verifyPermission(developerDeleteDTO.getLoginUser(), ProPermissionAliasEnum.DEVELOPER_DELETE.getAlias());
            String corpid = developerDeleteDTO.getCorpid();
            DeveloperEntity developer = developerModel.getByKey(developerDeleteDTO.getDeveloperId(), corpid);
            if (Objects.isNull(developer)) {
                throw new XbbException(ProErrorCodeEnum.API_ERROR_260030);
            }
            UserEntity userEntity = userModel.getByKeyIngoreDel(developer.getDeveloper(), corpid);
            if (Objects.isNull(userEntity)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100065);
            }
            developerModel.deleteByKey(developerDeleteDTO.getDeveloperId(), corpid);
            // 日志
            String userName= userVO.getName();
            String userId = userVO.getUserId();
            String memo = String.format(I18nMessageUtil.getMessage(LogMemoConstant.LOG_MEMO_USER_DELETE_DEVELOPER), userName, userEntity.getName());
            mongoLogHelp.buildLog(corpid, userId, userName, OperateModuleTypeEnum.DEVELOPER, OperateTypeEnum.DELETE,
                    developer.getId().toString(), userEntity.getName(), memo, developerDeleteDTO.getHttpHeader());
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("developerServiceImpl.update error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return developerUpdateVO;
    }

    @Override
    public DeveloperGetVO get(DeveloperGetDTO developerGetDTO) throws XbbException {
        DeveloperGetVO developerGetVO = new DeveloperGetVO();
        try {
            String corpid = developerGetDTO.getCorpid();
            DeveloperEntity developerEntity = developerModel.getByKey(developerGetDTO.getDeveloperId(), corpid);
            if (Objects.isNull(developerEntity)) {
                return developerGetVO;
            }
            Set<String> userSet = new HashSet<>();
            Set<Long> formIdList = new HashSet<>();
            Set<Long> workOrderFormIdList = new HashSet<>();
            userSet.add(developerEntity.getDeveloper());
            List<FormPojo> list1 = JSON.parseArray(developerEntity.getDevPermission(), FormPojo.class);
            if (CollectionsUtil.isNotEmpty(list1)) {
                list1.forEach(formPojo->{
                    if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType())) {
                        workOrderFormIdList.add(formPojo.getFormId());
                    } else {
                        formIdList.add(formPojo.getFormId());
                    }

                });
            }
            Map<Long, PaasFormEntity> formMap = proFormHelp.getFormMap(formIdList, corpid, null);
            Map<Long, WorkOrderFormEntity> workOrderFormMap = proFormHelp.getWorkOrderFormMap(workOrderFormIdList, corpid, null);
            List<UserEntity> userList =  userModel.findEntitysByUserIds(new ArrayList<>(userSet), corpid);
            Map<String, UserEntity> userMap = new HashMap<>(userList.size());
            userList.forEach(item->{
                userMap.put(item.getUserId(), item);
            });
            // set开发人员和开发权限范围
            DeveloperPojo developerPojo = setDeveloperAndPermission(userMap, workOrderFormMap, formMap, developerEntity, developerGetDTO.getLoginUser());
            developerGetVO.setDeveloper(developerPojo);
            return developerGetVO;
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOG.error("developerServiceImpl.get error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }

    }

    @Override
    public void verifyPermission(String corpid, Long formId, UserVO userVO, Integer saasMark, Integer businessType) throws XbbException {
        DeveloperVerifyDTO developerVerifyDTO = new DeveloperVerifyDTO();
        developerVerifyDTO.setCorpid(corpid);
        developerVerifyDTO.setLoginUser(userVO);
        developerVerifyDTO.setFormId(formId);
        developerVerifyDTO.setSaasMark(saasMark);
        developerVerifyDTO.setBusinessType(businessType);
        if (!hasDeveloperPermission(developerVerifyDTO)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100017);
        }
    }
    @Override
    public boolean hasDeveloperPermission(DeveloperVerifyDTO developerVerifyDTO) throws XbbException {
        try {
            String corpid = developerVerifyDTO.getCorpid();
            Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            param.put(StringConstant.CORPID, corpid);
            param.put(StringConstant.DEL, DelEnum.NORMAL.getDel());
            param.put("developer", developerVerifyDTO.getUserId());
            List<DeveloperEntity> list = developerModel.findEntitys(param);
            if (CollectionsUtil.isEmpty(list)) {
                return false;
            }
            for(DeveloperEntity item: list){
                List<FormPojo> formList = JSON.parseArray(item.getDevPermission(), FormPojo.class);
                for (FormPojo formPojo : formList) {
                    if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), developerVerifyDTO.getBusinessType())
                            && Objects.equals(formPojo.getFormId(), developerVerifyDTO.getFormId())) {
                        return true;
                    } else if (Objects.equals(formPojo.getFormId(), developerVerifyDTO.getFormId())) {
                        return true;
                    }
                }
            }
        }  catch (Exception e) {
            LOG.error("developerServiceImpl.hasDeveloperPermission error：", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
        return false;
    }

    /**
     * set开发人员和开发权限范围
     * @param userMap 人员id
     * @param workOrderFormMap 工单表单
     * @param formMap 除工单外的表单容器
     * @param developerEntity
     * @return void
     * @author 吴峰
     * @date 2021/3/4 22:06
     * @throws XbbException
     */
    private DeveloperPojo setDeveloperAndPermission(Map<String, UserEntity> userMap, Map<Long, WorkOrderFormEntity> workOrderFormMap, Map<Long, PaasFormEntity> formMap,
                                                    DeveloperEntity developerEntity, UserVO userVO) {
        DeveloperPojo developerPojo = new DeveloperPojo();
        developerPojo.setId(developerEntity.getId());
        developerPojo.setMemo(developerEntity.getMemo());
        DepartmentUserPojo userDepartmentEntity = new DepartmentUserPojo();
        if (userMap.containsKey(developerEntity.getDeveloper())) {
            userDepartmentEntity.setId(userMap.get(developerEntity.getDeveloper()).getUserId());
            userDepartmentEntity.setName(userMap.get(developerEntity.getDeveloper()).getName());
        }
        developerPojo.setUser(userDepartmentEntity);
        List<FormPojo> list1 = JSON.parseArray(developerEntity.getDevPermission(), FormPojo.class);
        if (CollectionsUtil.isNotEmpty(list1)) {
            Iterator<FormPojo> iterator = list1.iterator();
            while (iterator.hasNext()) {
                FormPojo formPojo = iterator.next();
                if (Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType()) && workOrderFormMap.containsKey(formPojo.getFormId())) {
                    formPojo.setName(workOrderFormMap.get(formPojo.getFormId()).getName());
                } else if (!Objects.equals(XbbRefTypeEnum.WORK_ORDER.getCode(), formPojo.getBusinessType()) && formMap.containsKey(formPojo.getFormId())) {
                    formPojo.setName(formMap.get(formPojo.getFormId()).getName());
                } else {
                    iterator.remove();
                }
            }
        }
        developerPojo.setFormList(list1);
        boolean update = userVO.isAdminOrBoss()
                || ProPermissionHelp.hasThisPermission(userVO, ProPermissionAliasEnum.DEVELOPER_EDIT.getAlias());
        developerPojo.setUpdate(update);
        boolean delete = userVO.isAdminOrBoss()
                || ProPermissionHelp.hasThisPermission(userVO, ProPermissionAliasEnum.DEVELOPER_DELETE.getAlias());
        developerPojo.setDelete(delete);
        return developerPojo;
    }
}
